package se.webbzon.boltzmann.minigame.chemistry;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

import se.webbzon.boltzmann.minigame.sandbox.DefaultSandBoxContainer;
import se.webbzon.boltzmann.minigame.sandbox.DefaultSandBoxItem;
import se.webbzon.boltzmann.minigame.sandbox.SandBox;
import se.webbzon.boltzmann.minigame.sandbox.SandBoxContainer;
import se.webbzon.boltzmann.minigame.sandbox.SandBoxIcon;
import se.webbzon.boltzmann.minigame.sandbox.SandBoxItem;
import se.webbzon.boltzmann.minigame.sandbox.SandBoxListener;
import se.webbzon.boltzmann.minigame.sandbox.SandBoxObject;
import se.webbzon.boltzmann.resource.Resources;
import se.webbzon.boltzmann.sound.SoundUtils;
import se.webbzon.oschi01.resourceset.ResourceSet;
import se.webbzon.oschi01.textureset.Texture3D;
import se.webbzon.oschi01.worldarea.WorldArea;
import se.webbzon.oschi01.worldcamera.WorldCameraPane;
import se.webbzon.oschi01.worldobject.WorldObject;

public class Electrolysis extends DefaultSandBoxItem {
	
	// The 2-dimensional depth of this electrolysis
	private static final int depth2d = 50;
	
	// The width and height of this electrolysis
	private static final int width = 96, height = 96;
	
	// The texture set of this electrylysis
	private final ResourceSet<Texture3D> textureSet;
	
	// The reaction containers of this electrylysis
	private final DefaultSandBoxContainer reagent, anode, catode;
	
	// The sand box listener of this electrylysis
	private final SandBoxListener listener;
	
	// The sand box to which this electrylysis is attached
	private SandBox box;
	
	/*============================================================
	Constructors
	============================================================*/
	
	public Electrolysis() {
		textureSet = Resources.getTextureSet("chemistry_electrolysis");
		reagent = new DefaultSandBoxContainer();
		anode = new DefaultSandBoxContainer();
		catode = new DefaultSandBoxContainer();
		listener = new SandBoxListenerImpl();
		setInstance(new Instance());
		setSize(width,height);
	}
	
	/*============================================================
	Public Methods
	============================================================*/
	
	/** Processes the provided reagents. If null is returned then no
	 * reaction occurred. **/
	public static final Product process(SandBoxMolecule[] reagents) {
		ArrayList<AmmoniaMolecule> ammonia = new ArrayList<AmmoniaMolecule>();
		ArrayList<WaterMolecule> water = new ArrayList<WaterMolecule>();
		for (SandBoxMolecule reagent : reagents) {
			if (reagent instanceof AmmoniaMolecule) 
				ammonia.add((AmmoniaMolecule) reagent);
			else if (reagent instanceof WaterMolecule) 
				water.add((WaterMolecule) reagent);
		}
		if (ammonia.size() == 0 && water.size() == 0)
			return null;
		
		int outHydrogen = 0;
		int outOxygen = 0;
		int outNitrogen = 0;
		int n;
		Collection<SandBoxMolecule> consumed = new ArrayList<SandBoxMolecule>();
		
		// Consume water
		n = water.size() % 2 == 0 ? water.size() : water.size() - 1;
		Iterator<WaterMolecule> wit = water.iterator();
		for (int i = 0; i < n; i++)
			consumed.add(wit.next());
		outHydrogen += n;
		outOxygen += n/2;
		
		// Consume ammonia
		n = ammonia.size() % 2 == 0 ? ammonia.size() : ammonia.size() - 1;
		Iterator<AmmoniaMolecule> ait = ammonia.iterator();
		for (int i = 0; i < n; i++)
			consumed.add(ait.next());
		outHydrogen += 3*(n/2);
		outNitrogen += n/2;
		
		if (consumed.isEmpty())
			return null;
		else {
			Product p = new Product();
			p.consumed = consumed.toArray(new SandBoxMolecule[0]);
			p.catode = new SandBoxMolecule[outHydrogen];
			for (int i = 0; i < outHydrogen; i++)
				p.catode[i] = new HydrogenMolecule();
			p.anode = new SandBoxMolecule[outOxygen + outNitrogen];
			for (int i = 0; i < outOxygen; i++)
				p.anode[i] = new OxygenMolecule();
			for (int i = outOxygen; i < outOxygen + outNitrogen; i++)
				p.anode[i] = new NitrogenMolecule();
			return p;
		}
	}
	
	/*============================================================
	Private Classes
	============================================================*/
	
	/** A product of this electrolysis. **/
	public static final class Product {
		SandBoxMolecule[] anode = null;
		SandBoxMolecule[] catode = null;
		SandBoxMolecule[] consumed = null;
	}
	
	/*============================================================
	Public Classes
	============================================================*/
	
	private final class Instance extends SandBoxObject {

		public Instance() {
			super(WorldObject.onPostDraw, depth2d);
		}

		@Override public void onAttach(SandBox box) {
			box.addItem(reagent);
			box.addItem(anode);
			box.addItem(catode);
			Electrolysis.this.box = box;
			box.addListener(listener);
		}

		@Override public void onDetach(SandBox box) {
			box.removeItem(reagent);
			box.removeItem(anode);
			box.removeItem(catode);
			box.removeListener(listener);
			Electrolysis.this.box = null;
		}

		@Override public void setPosition(double x, double y) {
			super.setPosition(x, y);
			reagent.setPosition(x, y + 80);
			anode.setPosition(x + 80, y);
			catode.setPosition(x - 80, y);
		}

		@Override public void onPostDraw(WorldArea world, WorldCameraPane hud) {
			int x = (int) getX() - width/2, y = (int) getY() - height/2;
			hud.drawTexture(textureSet.get(0), x, y, width, height);
		}

		@Override public SandBoxIcon createIcon() {
			return null;
		}
		
	}
	
	private final class SandBoxListenerImpl implements SandBoxListener {

		@Override public void onInvoke(SandBox box, double x, double y) {};

		@Override public void onPick(SandBox box, SandBoxItem item, SandBoxContainer container) {
			if (anode == container || catode == container) {
				process();
			}
		}

		@Override public void onDrop(SandBox box, SandBoxItem item, SandBoxContainer container) {
			if (reagent == container) {
				process();
			}
		}
		
		/** Triggers the electrolysis process. **/
		private void process() {
			if (anode.isEmpty() && catode.isEmpty()) {
				Collection<SandBoxItem> items = reagent.getContent();
				ArrayList<SandBoxMolecule> molecules = new ArrayList<SandBoxMolecule>();
				for (SandBoxItem item : items) {
					if (item instanceof SandBoxMolecule)
						molecules.add((SandBoxMolecule) item);
				}
				Product p = Electrolysis.process(molecules.toArray(new SandBoxMolecule[0]));
				if (p != null) {
					for (SandBoxMolecule m : p.anode)
						anode.putItem(box, m);
					for (SandBoxMolecule m : p.catode)
						catode.putItem(box, m); 
					for (SandBoxMolecule m : p.consumed) {
						reagent.removeContent(m);
					}
					SoundUtils.playSound("bubble_02");
				}
			}
		}
		
	}

}
